﻿using System.Runtime.InteropServices.WindowsRuntime;
using JumpstartApp.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using System.Reflection;
using Windows.UI.Xaml.Controls;
using System.Xml.Linq;
using JumpstartApp.Helpers;
using Windows.Storage;
using System.IO;
using System.Xml.Serialization;
using Windows.ApplicationModel.Search;
using Windows.Storage.Streams;
using System.Reactive;

namespace JumpstartApp.ViewModels
{
    using JumpstartApp;
    using JumpstartApp.Helpers;
    using JumpstartApp.Models;

    public class SearchViewModel : BindableBase
    {
        public SearchViewModel()
        {
            if (Windows.ApplicationModel.DesignMode.DesignModeEnabled)
            {
                LoadData_DesignTime();
            }
            else
            {
                LoadData_Runtime();
                this.PropertyChanged += (s, e) =>
                {
                    if (e.PropertyName.Equals("Selected") && this.Selected != null)
                    {
                        Services.Navigation.GotoDetail(this.Selected.Id);
                        this.Selected = null;
                    }
                };
            }
        }

        public static async void Suggest(SearchPaneSuggestionsRequestedEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("Suggest {0}", e.QueryText);

            if (e.Request.IsCanceled)
            {
                System.Diagnostics.Debug.WriteLine("Suggest Canceled");
                return;
            }

            var _Deferral = e.Request.GetDeferral();

            // combine show episodes with course modules
            var _Data = (await Data.RealData()).OfType<CourseRecord>().SelectMany(x => x.Children.Cast<ChildBase>())
                .Union((await JumpstartApp.Data.RealData()).OfType<ShowRecord>().SelectMany(x => x.Children.Cast<ChildBase>()));

            var _ResultSuggestions = _Data
                // limit to modules containing the string (title only)
                .Where(x => x.Title.ToLower().Contains(e.QueryText.ToLower()))
                // limit to one
               .Take(2);

            System.Diagnostics.Debug.WriteLine("Suggest {0} Results", _ResultSuggestions.Count());

            var _QueryLimit = 5;
            if (_ResultSuggestions.Any())
                _QueryLimit = 4 - _ResultSuggestions.Count();

            // look for query suggestions
            var _QuerySuggestions = _Data
                // limit to modules containing the string (title and desc)
                .Where(x => x.Title.ToLower().Contains(e.QueryText.ToLower()) || x.Description.ToLower().Contains(e.QueryText.ToLower()))
                // proejct list to only title and description contatenated
                .Select(x => string.Concat(x.Title, " ", x.Description))
                // project list into single words
                .SelectMany(x => x.ToLower().Split(' '))
                // remove non-word characters like comma
                .Select(x => System.Text.RegularExpressions.Regex.Replace(x, @"\W", string.Empty))
                // remove diplicates from the list
                .Distinct()
                // limit to words contianing the string
                .Where(x => x.ToLower().Contains(e.QueryText.ToLower()))
                // order by eariest appearance
                .OrderBy(x => x.ToLower().IndexOf(e.QueryText.ToLower()))
                // limit the list to the available space
                .Take(_QueryLimit);

            System.Diagnostics.Debug.WriteLine("Suggest {0} Queries", _QuerySuggestions.Count());

            // query suggestions
            if (e.Request.IsCanceled)
            {
                System.Diagnostics.Debug.WriteLine("Suggest Canceled");
                return; // the user may have typed something else
            }
            e.Request.SearchSuggestionCollection.AppendQuerySuggestions(_QuerySuggestions);

            // seperator line
            if (_ResultSuggestions.Any())
                e.Request.SearchSuggestionCollection.AppendSearchSeparator("Courses");

            // result suggestions
            foreach (var item in _ResultSuggestions)
            {
                if (e.Request.IsCanceled)
                {
                    System.Diagnostics.Debug.WriteLine("Suggest Canceled");
                    return; // the user may have typed something else
                }
                var _Resize = Windows.Storage.Streams.RandomAccessStreamReference.CreateFromUri(new Uri(item.Image));
                e.Request.SearchSuggestionCollection.AppendResultSuggestion(item.Title, item.Description, item.Id, _Resize, item.Title);
            }

            _Deferral.Complete();
        }

        private string m_LastSearch = string.Empty;
        public void Search(string text)
        {
            // prevent repeat searching
            if (m_LastSearch == text)
                return;
            m_LastSearch = text;

            this.Results.Clear();
            if (string.IsNullOrWhiteSpace(text))
            {
                this.SubTitle = "Ready...";
            }
            else
            {
                // find modules
                var _Modules = Records.OfType<CourseRecord>().SelectMany(x => x.Children);
                var _MResults = _Modules
                    .Where(x => x.Title.ToLower().Contains(text.ToLower())
                        || x.Description.ToLower().Contains(text.ToLower()))
                    .OrderBy(x => x.Title.ToLower().IndexOf(text.ToLower()));
                foreach (var item in _MResults)
                    this.Results.Add(item);

                // find modules
                var _Episodes = Records.OfType<ShowRecord>().SelectMany(x => x.Children).ToArray();
                foreach (var childBase in _Episodes)
                {
                    if (childBase.Title.ToLower().Contains(text.ToLower()))
                    {
                        var m = childBase;
                    }
                }
                var _EResults = _Episodes
                    .Where(x => x.Title.ToLower().Contains(text.ToLower())
                        || x.Description.ToLower().Contains(text.ToLower()))
                    .OrderBy(x => x.Title.ToLower().IndexOf(text.ToLower()));
                foreach (var item in _EResults)
                    this.Results.Add(item);

                foreach (var childBase in Results)
                {
                    childBase.ColSpan = 256;
                    childBase.RowSpan = 144;
                }

                this.SubTitle = string.Format("{0} results with \"{1}\"", Results.Count, text);
            }
        }

        void LoadData_DesignTime()
        {
            this.Records.Clear();
            foreach (var item in JumpstartApp.Data.SampleData())
                Records.Add(item);
            foreach (var item in this.Records.OfType<CourseRecord>().SelectMany(x => x.Children))
            {
                this.Results.Add(item);
            }
        }

        async void LoadData_Runtime()
        {
            this.Records.Clear();
            foreach (var item in (await JumpstartApp.Data.RealData()).OfType<CourseRecord>())
                Records.Add(item);
            foreach (var item in (await JumpstartApp.Data.RealData()).OfType<ShowRecord>())
                Records.Add(item);
        }

        string m_SubTitle = "Runtime Subtitle";
        public string SubTitle { get { return m_SubTitle; } set { SetProperty(ref m_SubTitle, value); } }

        ObservableCollection<ParentBase> m_Records = new ObservableCollection<ParentBase>();
        private ObservableCollection<ParentBase> Records { get { return m_Records; } }

        ObservableCollection<ChildBase> m_Results = new ObservableCollection<ChildBase>();
        public ObservableCollection<ChildBase> Results { get { return m_Results; } }

        Models.ChildBase m_Selected = default(Models.ChildBase);
        public Models.ChildBase Selected { get { return m_Selected; } set { SetProperty(ref m_Selected, value); } }
    }
}
